﻿using IdentityServer4.EntityFramework.Entities;
using Microsoft.EntityFrameworkCore;
using ApiResource = IdentityServer4.EntityFramework.Entities.ApiResource;

namespace Devonline.Identity.Admin;

public class ApiResourceService
{
    private readonly ILogger<ApiResourceService> _logger;
    private readonly ConfigurationDbContext _context;
    private readonly HttpContext _httpContext;
    private readonly HttpRequest _request;
    public ApiResourceService(
        ILogger<ApiResourceService> logger,
        ConfigurationDbContext context,
        IHttpContextAccessor httpContextAccessor
        )
    {
        if (httpContextAccessor.HttpContext == null)
        {
            throw new ArgumentNullException(nameof(httpContextAccessor.HttpContext), "HttpContext can't be null!");
        }

        _logger = logger;
        _context = context;
        _httpContext = httpContextAccessor.HttpContext;
        _request = _httpContext.Request;
    }

    public virtual async Task<PagedResult<ApiResource>> GetApiResourcesAsync(string search) => await _context.ApiResources.WhereIf(!string.IsNullOrEmpty(search), x => x.Name.Contains(search)).PageByAsync(_request.GetPagedRequest());

    public virtual Task<ApiResource> GetApiResourceAsync(int apiResourceId)
    {
        return _context.ApiResources
            .Include(x => x.UserClaims)
            .Include(x => x.Scopes)
            .Where(x => x.Id == apiResourceId)
            .AsNoTracking()
            .SingleOrDefaultAsync();
    }

    public virtual async Task<PagedResult<ApiResourceProperty>> GetApiResourcePropertiesAsync(int apiResourceId) => await _context.ApiResourceProperties.Where(x => x.ApiResource.Id == apiResourceId).PageByAsync(_request.GetPagedRequest());

    public virtual Task<ApiResourceProperty> GetApiResourcePropertyAsync(int apiResourcePropertyId)
    {
        return _context.ApiResourceProperties
            .Include(x => x.ApiResource)
            .Where(x => x.Id == apiResourcePropertyId)
            .SingleOrDefaultAsync();
    }

    public virtual async Task<int> AddApiResourcePropertyAsync(int apiResourceId, ApiResourceProperty apiResourceProperty)
    {
        var apiResource = await _context.ApiResources.Where(x => x.Id == apiResourceId).SingleOrDefaultAsync();

        apiResourceProperty.ApiResource = apiResource;
        await _context.ApiResourceProperties.AddAsync(apiResourceProperty);

        return await _context.SaveChangesAsync();
    }

    public virtual async Task<int> DeleteApiResourcePropertyAsync(ApiResourceProperty apiResourceProperty)
    {
        var propertyToDelete = await _context.ApiResourceProperties.Where(x => x.Id == apiResourceProperty.Id).SingleOrDefaultAsync();

        _context.ApiResourceProperties.Remove(propertyToDelete);
        return await _context.SaveChangesAsync();
    }

    public virtual async Task<bool> CanInsertApiResourceAsync(ApiResource apiResource)
    {
        if (apiResource.Id == 0)
        {
            var existsWithSameName = await _context.ApiResources.Where(x => x.Name == apiResource.Name).SingleOrDefaultAsync();
            return existsWithSameName == null;
        }
        else
        {
            var existsWithSameName = await _context.ApiResources.Where(x => x.Name == apiResource.Name && x.Id != apiResource.Id).SingleOrDefaultAsync();
            return existsWithSameName == null;
        }
    }

    public virtual async Task<bool> CanInsertApiResourcePropertyAsync(ApiResourceProperty apiResourceProperty)
    {
        var existsWithSameName = await _context.ApiResourceProperties.Where(x => x.Key == apiResourceProperty.Key
                                                                                   && x.ApiResource.Id == apiResourceProperty.ApiResourceId).SingleOrDefaultAsync();
        return existsWithSameName == null;
    }

    /// <summary>
    /// Add new api resource
    /// </summary>
    /// <param name="apiResource"></param>
    /// <returns>This method return new api resource id</returns>
    public virtual async Task<int> AddApiResourceAsync(ApiResource apiResource)
    {
        _context.ApiResources.Add(apiResource);

        await _context.SaveChangesAsync();

        return apiResource.Id;
    }

    private async Task RemoveApiResourceClaimsAsync(ApiResource apiResource)
    {
        //Remove old api resource claims
        var apiResourceClaims = await _context.ApiResourceClaims.Where(x => x.ApiResource.Id == apiResource.Id).ToListAsync();
        _context.ApiResourceClaims.RemoveRange(apiResourceClaims);
    }

    private async Task RemoveApiResourceScopesAsync(ApiResource apiResource)
    {
        //Remove old api resource scopes
        var apiResourceScopes = await _context.ApiResourceScopes.Where(x => x.ApiResource.Id == apiResource.Id).ToListAsync();
        _context.ApiResourceScopes.RemoveRange(apiResourceScopes);
    }

    public virtual async Task<int> UpdateApiResourceAsync(ApiResource apiResource)
    {
        //Remove old relations
        await RemoveApiResourceClaimsAsync(apiResource);
        await RemoveApiResourceScopesAsync(apiResource);

        //Update with new data
        _context.ApiResources.Update(apiResource);

        return await _context.SaveChangesAsync();
    }

    public virtual async Task<int> DeleteApiResourceAsync(ApiResource apiResource)
    {
        var resource = await _context.ApiResources.Where(x => x.Id == apiResource.Id).SingleOrDefaultAsync();

        _context.Remove(resource);

        return await _context.SaveChangesAsync();
    }


    public virtual async Task<PagedResult<ApiResourceSecret>> GetApiSecretsAsync(int apiResourceId) => await _context.ApiSecrets.Where(x => x.ApiResource.Id == apiResourceId).PageByAsync(_request.GetPagedRequest());

    public virtual Task<ApiResourceSecret> GetApiSecretAsync(int apiSecretId)
    {
        return _context.ApiSecrets
            .Include(x => x.ApiResource)
            .Where(x => x.Id == apiSecretId)
            .AsNoTracking()
            .SingleOrDefaultAsync();
    }

    public virtual async Task<int> AddApiSecretAsync(int apiResourceId, ApiResourceSecret apiSecret)
    {
        apiSecret.ApiResource = await _context.ApiResources.Where(x => x.Id == apiResourceId).SingleOrDefaultAsync();
        await _context.ApiSecrets.AddAsync(apiSecret);

        return await _context.SaveChangesAsync();
    }

    public virtual async Task<int> DeleteApiSecretAsync(ApiResourceSecret apiSecret)
    {
        var apiSecretToDelete = await _context.ApiSecrets.Where(x => x.Id == apiSecret.Id).SingleOrDefaultAsync();
        _context.ApiSecrets.Remove(apiSecretToDelete);

        return await _context.SaveChangesAsync();
    }

    public virtual async Task<int> SaveAllChangesAsync()
    {
        return await _context.SaveChangesAsync();
    }

    public virtual async Task<string> GetApiResourceNameAsync(int apiResourceId)
    {
        var apiResourceName = await _context.ApiResources.Where(x => x.Id == apiResourceId).Select(x => x.Name).SingleOrDefaultAsync();

        return apiResourceName;
    }
}